package com.bstek.uflo.designer.view.security;

import java.util.Date;
import java.util.List;
import java.util.UUID;

import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;

import com.bstek.dorado.annotation.DataProvider;
import com.bstek.dorado.annotation.DataResolver;
import com.bstek.dorado.data.entity.EntityState;
import com.bstek.dorado.data.entity.EntityUtils;
import com.bstek.dorado.data.provider.Criteria;
import com.bstek.dorado.data.provider.Page;
import com.bstek.dorado.util.Assert;
import com.bstek.uflo.command.CommandService;
import com.bstek.uflo.designer.command.DeleteObjectCommand;
import com.bstek.uflo.designer.command.QueryPageCommand;
import com.bstek.uflo.designer.command.QueryUniqueResultCommand;
import com.bstek.uflo.designer.command.SaveObjectCommand;
import com.bstek.uflo.designer.command.UpdateObjectCommand;
import com.bstek.uflo.designer.security.command.DeleteProcessEntryCascadedCommand;
import com.bstek.uflo.designer.security.manager.SecurityManager;
import com.bstek.uflo.designer.security.model.NodeAttribute;
import com.bstek.uflo.designer.security.model.NodeEntry;
import com.bstek.uflo.designer.security.model.ProcessAttribute;
import com.bstek.uflo.designer.security.model.ProcessEntry;
import com.bstek.uflo.designer.security.model.ProcessEntryAssignee;
import com.bstek.uflo.model.ProcessDefinition;

/**
 * @author matt.yao@bstek.com
 * @since 1.0
 */
@Controller(UfloProcessSecurityMaintain.BEAN_ID)
public class UfloProcessSecurityMaintain {

	public static final String BEAN_ID = "ufloProcessSecurityMaintain";

	@Autowired
	@Qualifier(CommandService.BEAN_ID)
	private CommandService commandService;

	@Autowired
	@Qualifier(SecurityManager.BEAN_ID)
	private SecurityManager securityManager;

	@DataProvider
	public void loadProcessEntry(Page<ProcessEntry> page, Criteria criteria) {
		DetachedCriteria dc = DetachedCriteria.forClass(ProcessEntry.class);
		//dc.addOrder(Order.desc("createDate"));
		commandService.executeCommand(new QueryPageCommand(dc, page));
	}

	@DataProvider
	public void loadProcessEntryAssignee(Page<ProcessEntryAssignee> page, String processEntryId) {
		Assert.notNull(processEntryId);
		DetachedCriteria dc = DetachedCriteria.forClass(ProcessEntryAssignee.class);
		dc.add(Restrictions.eq("processEntryId", processEntryId));
		commandService.executeCommand(new QueryPageCommand(dc, page));
	}

	@DataResolver
	public void saveProcessEntry(List<ProcessEntry> processEntryList) throws Exception {
		for (ProcessEntry processEntry : processEntryList) {
			EntityState state = EntityUtils.getState(processEntry);
			if (state.equals(EntityState.NEW)) {
				processEntry.setId(UUID.randomUUID().toString());
				processEntry.setCreateDate(new Date());
				commandService.executeCommand(new SaveObjectCommand<ProcessEntry>(processEntry));
			}
			if (state.equals(EntityState.MODIFIED)) {
				commandService.executeCommand(new UpdateObjectCommand<ProcessEntry>(processEntry));
			}
			if (state.equals(EntityState.DELETED)) {
				commandService.executeCommand(new DeleteProcessEntryCascadedCommand(processEntry.getId()));
			}
			if (processEntry.getProcessAttributeList() != null) {
				this.saveProcessAttribute(processEntry);
			}
			if (processEntry.getNodeEntrylist() != null) {
				this.saveNodeEntry(processEntry);
			}
			if (processEntry.getProcessEntryAssigneeList() != null) {
				this.saveProcessEntryAssignee(processEntry);
			}
		}
	}

	public void saveNodeEntry(ProcessEntry processEntry) throws Exception {
		for (NodeEntry nodeEntry : processEntry.getNodeEntrylist()) {
			nodeEntry.setProcessEntryId(processEntry.getId());
			EntityState state = EntityUtils.getState(nodeEntry);
			if (state.equals(EntityState.MODIFIED)) {
				NodeEntry dbNodeEntry = securityManager.findDbNodeEntryByNodeName(processEntry.getId(), nodeEntry.getName());
				if (dbNodeEntry == null) {
					commandService.executeCommand(new UpdateObjectCommand<NodeEntry>(nodeEntry));
				} else {
					dbNodeEntry.setRemovable(nodeEntry.isRemovable());
					dbNodeEntry.setModifiable(nodeEntry.isModifiable());
					commandService.executeCommand(new UpdateObjectCommand<NodeEntry>(dbNodeEntry));
				}
			}
			if (state.equals(EntityState.NONE)) {
				DetachedCriteria dc = DetachedCriteria.forClass(NodeEntry.class);
				dc.add(Restrictions.eq("id", nodeEntry.getId()));
				NodeEntry dbNodeEntry = commandService.executeCommand(new QueryUniqueResultCommand<NodeEntry>(dc));
				if (dbNodeEntry == null) {
					commandService.executeCommand(new UpdateObjectCommand<NodeEntry>(nodeEntry));
				}
			}
			if (nodeEntry.getNodeAttributeList() != null) {
				this.saveNodeAttribute(nodeEntry);
			}
		}
	}

	public void saveNodeAttribute(NodeEntry nodeEntry) throws Exception {
		for (NodeAttribute nodeAttribute : nodeEntry.getNodeAttributeList()) {
			nodeAttribute.setNodeEntryId(nodeEntry.getId());
			EntityState state = EntityUtils.getState(nodeAttribute);
			if (state.equals(EntityState.MODIFIED)) {
				boolean exist = false;
				List<NodeAttribute> dbNodeAttribute = securityManager.findDbNodeAttribute(nodeEntry.getId());
				if (dbNodeAttribute != null) {
					for (NodeAttribute na : dbNodeAttribute) {
						if (na.getName().equals(nodeAttribute.getName())) {
							na.setAuthorityType(nodeAttribute.getAuthorityType());
							exist = true;
							commandService.executeCommand(new UpdateObjectCommand<NodeAttribute>(na));
						}
					}
				}
				if (!exist) {
					commandService.executeCommand(new UpdateObjectCommand<NodeAttribute>(nodeAttribute));
				}
			}
		}
	}

	public void saveProcessAttribute(ProcessEntry processEntry) throws Exception {
		for (ProcessAttribute processAttribute : processEntry.getProcessAttributeList()) {
			processAttribute.setProcessEntryId(processEntry.getId());
			EntityState state = EntityUtils.getState(processAttribute);
			if (state.equals(EntityState.MODIFIED)) {
				boolean exist = false;
				List<ProcessAttribute> dbProcessAttribute = securityManager.findDbProcessAttribute(processEntry.getId());
				if (dbProcessAttribute != null) {
					for (ProcessAttribute pa : dbProcessAttribute) {
						if (pa.getName().equals(processAttribute.getName())) {
							pa.setAuthorityType(processAttribute.getAuthorityType());
							exist = true;
							commandService.executeCommand(new UpdateObjectCommand<ProcessAttribute>(pa));
						}
					}
				}
				if (!exist) {
					commandService.executeCommand(new UpdateObjectCommand<ProcessAttribute>(processAttribute));
				}
			}
		}
	}

	public void saveProcessEntryAssignee(ProcessEntry processEntry) {
		for (ProcessEntryAssignee processEntryAssignee : processEntry.getProcessEntryAssigneeList()) {
			EntityState state = EntityUtils.getState(processEntryAssignee);
			if (state.equals(EntityState.NEW)) {
				processEntryAssignee.setId(UUID.randomUUID().toString());
				processEntryAssignee.setProcessEntryId(processEntry.getId());
				commandService.executeCommand(new SaveObjectCommand<ProcessEntryAssignee>(processEntryAssignee));
			}
			if (state.equals(EntityState.MODIFIED)) {
				commandService.executeCommand(new UpdateObjectCommand<ProcessEntryAssignee>(processEntryAssignee));
			}
			if (state.equals(EntityState.DELETED)) {
				commandService.executeCommand(new DeleteObjectCommand<ProcessEntryAssignee>(processEntryAssignee));
			}
		}
	}

	@DataProvider
	public List<ProcessDefinition> findProcess() throws Exception {
		return securityManager.findDbProcess();
	}

	@DataProvider
	public List<NodeEntry> findNodeEntry(String processEntryId, long processId) throws Exception {
		Assert.notNull(processId);
		return securityManager.findNodeEntryByProcessId(processEntryId, processId);
	}

	@DataProvider
	public List<NodeAttribute> findNodeAttribute(String nodeEntryId, String nodeType) throws Exception {
		Assert.notNull(nodeEntryId);
		return securityManager.findNodeAttributeByNodeType(nodeEntryId, nodeType);
	}

	@DataProvider
	public List<ProcessAttribute> findProcessAttribute(String processEntryId) throws Exception {
		Assert.notNull(processEntryId);
		return securityManager.findProcessAttribute(processEntryId);
	}
	
	
}
